/*
 * ProjectViewFactory.java
 * 
 * last update: 14.01.2010 by Stefan Saru
 * 
 * author:	Felix(felix@ulise.cs.pub.ro)
 * 			Luci
 *
 * Obs: N/A
 */

package ide;

import java.util.*;
import javax.swing.*;
import javax.swing.tree.*;
import java.io.*;


public class ProjectViewFactory {
	private String fileName;
	private DefaultMutableTreeNode root;
	private boolean isValid;
	private Project project;



	public ProjectViewFactory(Project project, String fileName,
			TreeModel treeModel) {
		this.project = project;
		File file = new File(fileName);
		isValid = true;
		if (!file.exists()) {
			//todo theProject.msg("File " + fileName + " not found");
			project.error("file \"" + fileName + "\" not found.");
			isValid = false;
		}
		this.fileName = file.getAbsolutePath();  //make sure that we get an absolute path.
		root = (DefaultMutableTreeNode) ((DefaultTreeModel)treeModel).getRoot();
	}

	//(15.03.1999 00:31:32) alec->
	//updated on 07.07.99
	/**
	 * addds all the ports in the current port declaration statement
	 * it is assumed that stk has just passed the "input", "output" or "inout" token
	 */
	protected void addPort(String currentFile, DefaultMutableTreeNode module, int portType, 
			StreamTokenizer stk)throws IOException{
		String name = null;
		do{
			stk.nextToken();
			if(stk.ttype == StreamTokenizer.TT_EOF)break;
			if(stk.ttype == StreamTokenizer.TT_WORD){
				name = stk.sval;
			}else{
				//skip sizes & stuff (the port name is rigth before a ',' or ';')
				if((stk.ttype == ',') || (stk.ttype == ';')){
					if(name == null)name = "??";
					DefaultMutableTreeNode n = new DefaultMutableTreeNode(
							new ProjectTreeNode(currentFile, name, stk.lineno(), portType));
					module.add(n);

				}
			}
		}while(stk.ttype != ';');
	}


	/**
	 * force stk to skip a pair of right braces [....]
	 */
	protected void skipBrace(StreamTokenizer stk)throws IOException{
		stk.nextToken();
		if(stk.ttype == StreamTokenizer.TT_EOF)return;
		if(stk.ttype != '[')return;
		while(stk.ttype != StreamTokenizer.TT_EOF){
			stk.nextToken();
			if(stk.ttype == ']')break;
		}
	}

	protected char getMatchingBrace(char brace){
		switch(brace){
		case '[': return ']';
		case '(': return ')';
		case '{': return '}';
		}
		throw new Error("weird brace: " + brace);
	}

	/**
	 * Gets stk over the matching brace of the given one.
	 */
	protected void skipToMatchingBrace(StreamTokenizer stk, char brace)throws IOException{
		char match = getMatchingBrace(brace);
		while(true){
			stk.nextToken();
			if(stk.ttype == match) return;
			if(stk.ttype ==  StreamTokenizer.TT_EOF)return;
		}
	}

	/**
	 * Consumes token until the given char token is reached
	 */
	protected void skipTo(StreamTokenizer stk, char to)throws IOException{
		while(true){
			stk.nextToken();
			if(stk.ttype == to){
				stk.nextToken();
				return;
			}
			if(stk.ttype ==  StreamTokenizer.TT_EOF)return;
		}
	}

	static boolean __debug__ = false;
	protected void debug(String s){
		if(__debug__)System.out.println(s);
	}

	/**
	 * Inserts all the stuff needed into the project tree
	 */
	public void updateProjectTree () throws Exception{
		//searches through the tree looking for a previous
		//description of the current file then it removes it.
		if (!isValid) {
			return;
		}
		ProjectTreeNode nodeInfo = new ProjectTreeNode(fileName, fileName, 1, ProjectTreeNode.FILETYPE);

		//remove this file's subtree, if any
		int index = 0;
		for(Enumeration e = root.children() ; e.hasMoreElements() ; index++){
			DefaultMutableTreeNode tn = (DefaultMutableTreeNode)e.nextElement();
			if(((ProjectTreeNode)tn.getUserObject()).equals(nodeInfo)){
				root.remove(index);
				break;
			}
		}

		DefaultMutableTreeNode upperNode = new DefaultMutableTreeNode(nodeInfo);
		DefaultMutableTreeNode localNode = null;
		DefaultMutableTreeNode fileNode = upperNode; //used for include's inserting

		FileReader fr;
		StreamTokenizer stk = new StreamTokenizer(fr = new ProgressFileReader(fileName));
		stk.slashStarComments(true);
		stk.slashSlashComments(true);
		stk.wordChars('_', '_');
		stk.wordChars('`', '`');
		stk.wordChars('.', '.');
		stk.ordinaryChar('\'');

		//keeps the scopeNodes as they are imbricated inside the current module
		Stack scopes = new Stack();
		//keeps the number of nested noname blocks inside a named one
		Stack nestedBlock = new Stack();
		//nested `include ' s
		Stack tokenizers = new Stack(), readers = new Stack(), fileNames = new Stack();
		String currentFile = fileName;

		//counts nested begin..end nested blocks between two begin:<name> nested ones, like this:
		//begin : block1
		//  begin
		//    begin
		//      begin : block2
		//        some stuff here...
		//      end //for block2
		//    end
		//  end
		//end //for block1
		int localNestedBlocks = 0;

		//this is some sort of a lexical state
		boolean inModule = false;

		do{
			stk.nextToken();
			debug("token: " + (stk.ttype == StreamTokenizer.TT_WORD ? stk.sval
					: (new Character((char) stk.ttype)).toString()) + " type: " + stk.ttype);
			if(stk.ttype == StreamTokenizer.TT_WORD){
				if(stk.sval.equals("module") || stk.sval.equals("primitive")){

					//          if(localNode != null)upperNode.add(localNode);
					//put all of the remaining scopes into the current Module
					if(inModule){
						while(!scopes.empty()){
							//move down into the stack
							upperNode.add(localNode); //add the scope into his ancestor
							localNode = upperNode;
							upperNode = (DefaultMutableTreeNode) scopes.pop();
							int type = ((ProjectTreeNode) localNode.getUserObject()).type;
							if(type == ProjectTreeNode.MODULETYPE)break;
							inModule = false;
						} //here, upperNode should contain the outermost scope ( the file's)
						upperNode.add(localNode); //this should be the previous module ??
					}
					stk.nextToken();
					debug("token: " + (stk.ttype == StreamTokenizer.TT_WORD ? stk.sval
							: (new Character((char) stk.ttype)).toString()) + " type: " + stk.ttype);
					if(stk.ttype == StreamTokenizer.TT_EOF)break;
					localNode = new DefaultMutableTreeNode(new ProjectTreeNode(currentFile, stk.sval, stk.lineno(),
							ProjectTreeNode.MODULETYPE));
					inModule = true;
				}else
					if(stk.sval.equals("`include")){ //include directive
						debug("include");
						stk.wordChars('/', '/');
						stk.wordChars('\\', '\\');  //MSDOS separator
						stk.wordChars('\"', '\"');
						stk.nextToken();
						debug("token: " + (stk.ttype == StreamTokenizer.TT_WORD ? stk.sval
								: (new Character((char) stk.ttype)).toString()) +
								" type: " + stk.ttype);
						stk.ordinaryChar('/');
						stk.ordinaryChar('\\');
						stk.ordinaryChar('\"');
						if(stk.ttype == StreamTokenizer.TT_WORD){
							String file = stk.sval;
							String nodeName;
							nodeName = file = file.substring(1, file.length() - 1);
							debug(file);
							if (!new File(file).isAbsolute()) {
								file = fileName.substring(0,
										fileName.lastIndexOf(File.separatorChar) +
										1)
										+ file;
							}
							try{
								FileReader fr1;
								debug("fileName = " + file);
								StreamTokenizer stk1 = new StreamTokenizer(fr1 = new FileReader(file));
								tokenizers.push(stk);
								readers.push(fr);
								fileNames.push(currentFile);
								stk = stk1;
								fr = fr1;
								currentFile = file;
								fileNode.add (new DefaultMutableTreeNode(new
										ProjectTreeNode(currentFile, nodeName,
												1, ProjectTreeNode.INCLUDETYPE)));
							}catch(FileNotFoundException fex){
								project.error("included file \"" + file + "\" not found.");
							}
						} 
					}else
						//=================== module contents ========================================
						if(inModule){
							if(stk.sval.equals("task")){
								scopes.push(upperNode);
								upperNode = localNode;
								stk.nextToken();
								debug("token: " + (stk.ttype == StreamTokenizer.TT_WORD ? stk.sval
										: (new Character((char) stk.ttype)).toString()) + " type: " + stk.ttype);
								if(stk.ttype == StreamTokenizer.TT_EOF)break;
								localNode = new DefaultMutableTreeNode(new ProjectTreeNode(currentFile, stk.sval, stk.lineno(),
										ProjectTreeNode.TASKTYPE));
							}else
								if(stk.sval.equals("function")){
									scopes.push(upperNode);
									upperNode = localNode;
									stk.nextToken();
									debug("token: " + (stk.ttype == StreamTokenizer.TT_WORD ? stk.sval
											: (new Character((char) stk.ttype)).toString()) + " type: " + stk.ttype);
									if(stk.ttype == StreamTokenizer.TT_EOF)break;
									localNode = new DefaultMutableTreeNode(new ProjectTreeNode(currentFile, stk.sval, stk.lineno(),
											ProjectTreeNode.FUNCTIONTYPE));
								}else
									if(stk.sval.equals("endfunction") || stk.sval.equals("endtask")){
										upperNode.add(localNode);
										localNode = upperNode;
										upperNode = (DefaultMutableTreeNode)scopes.pop();
									}else
										if(stk.sval.equals("begin") || stk.sval.equals("fork")){
											stk.nextToken();
											debug("token: " + (stk.ttype == StreamTokenizer.TT_WORD ? stk.sval
													: (new Character((char) stk.ttype)).toString()) + " type: " + stk.ttype);
											if(stk.ttype == StreamTokenizer.TT_EOF)break;
											if(stk.ttype == ':'){
												stk.nextToken();
												if(stk.ttype == StreamTokenizer.TT_EOF)break;
												nestedBlock.push(new Integer(localNestedBlocks));
												localNestedBlocks = 0;
												scopes.push(upperNode);
												upperNode = localNode;
												localNode = new DefaultMutableTreeNode(new ProjectTreeNode(currentFile, stk.sval, stk.lineno(),
														ProjectTreeNode.BLOCKTYPE));
											}else{
												if(stk.ttype == StreamTokenizer.TT_WORD && stk.sval == "end"){/*a stupid begin end*/}
												else if(((ProjectTreeNode)localNode.getUserObject()).getType() == ProjectTreeNode.BLOCKTYPE){
													//a noname begin inside a named block
													localNestedBlocks++;
													stk.pushBack();
												}
											}
										}else
											if(stk.sval.equals("end")){
												if(((ProjectTreeNode)localNode.getUserObject()).getType() == ProjectTreeNode.BLOCKTYPE){
													if(localNestedBlocks == 0){
														upperNode.add(localNode);
														localNode = upperNode;
														upperNode = (DefaultMutableTreeNode) scopes.pop();
														localNestedBlocks = ((Integer) nestedBlock.pop()).intValue();
													}
												}
											}else
												if(stk.sval.equals("input")){
													addPort(currentFile, localNode, ProjectTreeNode.INPUTTYPE, stk);
												}else
													if(stk.sval.equals("output")){
														addPort(currentFile, localNode, ProjectTreeNode.OUTPUTTYPE, stk);
													}else
														if(stk.sval.equals("inout")){
															addPort(currentFile, localNode, ProjectTreeNode.INOUTTYPE, stk);
														}else
															if(stk.sval.equals("reg")){
																String name = null;
																do{
																	skipBrace(stk);
																	if(stk.ttype == StreamTokenizer.TT_WORD){
																		name = stk.sval;
																	}else
																		if((stk.ttype == ',') || (stk.ttype == ';')){
																			if(name == null)name = "??";
																			DefaultMutableTreeNode n = new DefaultMutableTreeNode(
																					new ProjectTreeNode(currentFile, name, stk.lineno(), ProjectTreeNode.REGTYPE));
																			localNode.add(n);
																		}
																}while(stk.ttype != ';');
															}else
																if(stk.sval.equals("wire") || stk.sval.startsWith("tri") ||
																		stk.sval.equals("wand") || stk.sval.equals("wor") ||
																		stk.sval.equals("supply1") || stk.sval.equals("supply0") ||
																		stk.sval.equals("supply1") || stk.sval.equals("supply0")){
																	debug("net type: " + stk.sval);
																	String name = null;
																	stk.nextToken();
																	if(stk.ttype == StreamTokenizer.TT_WORD && 
																			(stk.sval.equals("vectored") ||
																					stk.sval.equals("scalared")))stk.nextToken();

																	debug("skipping trash: " + stk.ttype);
																	if(stk.ttype == '[')skipTo(stk, ']'); //skip strengths
																	if(stk.ttype == '#'){
																		debug("delays");
																		stk.nextToken();
																		if(stk.ttype == '(')skipTo(stk, ')'); //skip delay2/3
																		debug("token after delays: " + (stk.ttype == StreamTokenizer.TT_WORD ? stk.sval
																				: (new Character((char) stk.ttype)).toString()) + " type: " + stk.ttype);
																	}
																	stk.pushBack();
																	do{
																		stk.nextToken();
																		debug("token: " + (stk.ttype == StreamTokenizer.TT_WORD ? stk.sval
																				: (new Character((char) stk.ttype)).toString()) + " type: " + stk.ttype);
																		if(stk.ttype == StreamTokenizer.TT_WORD){
																			name = stk.sval;
																		}else{                
																			if((stk.ttype == '=') || (stk.ttype == ',') || (stk.ttype == ';')){
																				if(stk.ttype == '='){
																					while((stk.ttype != ',') && (stk.ttype != ';') &&
																							(stk.ttype != StreamTokenizer.TT_EOF)){
																						stk.nextToken(); //skip assign
																						debug("token: " + (stk.ttype == StreamTokenizer.TT_WORD ? stk.sval
																								: (new Character((char)
																										stk.ttype)).toString()) + " type: " + stk.ttype);
																					}
																				}
																				if(name == null)name = "??";
																				DefaultMutableTreeNode n = new
																				DefaultMutableTreeNode( new ProjectTreeNode(currentFile, name, 
																						stk.lineno(),
																						ProjectTreeNode.WIRETYPE));
																				localNode.add(n);
																			}
																		}
																	}while(stk.ttype != ';');
																}else
																	if(stk.sval.equals("endmodule") || stk.sval.equals("endprimitive")){
																		if(inModule)
																			//put all of the remaining scopes into the currentModule
																			//(a sort of flush)
																			while(!scopes.empty()){
																				//move down into the stack
																				upperNode.add(localNode); //adds the scope into his ancestor
																				localNode = upperNode;
																				upperNode = (DefaultMutableTreeNode)scopes.pop();
																				int type = ((ProjectTreeNode) localNode.getUserObject()).type;
																				if(type == ProjectTreeNode.MODULETYPE)break;
																			} //here, upperNode should contain the outermost scope ( the file's)
																		upperNode.add(localNode);
																		localNode = null; //secure this node
																		//            upperNode.add(localNode); //this should have been done in that while !
																		inModule = false;
																	}
						}
			}else
				if(stk.ttype == StreamTokenizer.TT_EOF &&
						!tokenizers.empty()){
					//move down into the stack
					debug("End of file:");
					stk = (StreamTokenizer) tokenizers.pop();
					try{fr.close();}catch(IOException ioex){} //ei, Power, cum e ?
					fr = (FileReader) readers.pop();
					currentFile = (String)fileNames.pop();
				}
		}while(stk.ttype != StreamTokenizer.TT_EOF);
		try{fr.close();}catch(IOException ioex){}
		if(localNode != null)upperNode.add(localNode);
		while(!scopes.empty()){
			//move down into the stack
			localNode = upperNode;
			upperNode = (DefaultMutableTreeNode)scopes.pop();
			upperNode.add(localNode); //add the scope into his ancestor
		}
		root.add(upperNode);
	}

	class ProgressFileReader extends FileReader {

		public ProgressFileReader(String s) throws FileNotFoundException{
			super(s);
		}

		public int read (char[] cbuf, int off,
				int len) throws IOException
				{
			project.sb.incPBar(len);
			return super.read(cbuf, off, len);
				}

	}
}
//(15.03.1999 00:31:33) <-alec

